package org.jumiz.telnet;

import org.jumiz.telnet.command.CommandEnum;
import org.jumiz.telnet.command.ExecuteCommand;
import org.jumiz.telnet.command.MessageResponse;
import org.jumiz.telnet.log.Log;
import org.jumiz.telnet.log.LogFactory;
import org.jumiz.telnet.play.AbstractPlay;
import org.jumiz.telnet.play.GuessingNumbers;
import org.jumiz.telnet.play.GuessingNumbersPlus;
import org.jumiz.telnet.session.SocketSession;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 */
public class Main {

    static Log log = LogFactory.getLog(Main.class);

    public static void main(String[] args) {

        AbstractPlay.register("1", new GuessingNumbers());
        AbstractPlay.register("2", new GuessingNumbersPlus());

        SocketSession socketSession = new SocketSession();

        int port = 9999;
        try {
            // 创建服务端Channel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 设置非阻塞
            serverSocketChannel.configureBlocking(false);
            // 创建Selector
            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            // 绑定端口
            serverSocketChannel.bind(new InetSocketAddress(port));

            while (true) {
                // 当没有事件到来的时候，这里是阻塞的,有事件的时候会自动运行
                selector.select();
                // 如果有事件到来，这里可以得到注册到该selector上的所有的key，每一个key上都有一个channel
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    iterator.remove();
                    try {
                        // 连接上来了
                        if (selectionKey.isAcceptable()) {
                            ServerSocketChannel serverChannel = (ServerSocketChannel)selectionKey.channel();
                            SocketChannel clientChannel = serverChannel.accept();
                            clientChannel.configureBlocking(false);
                            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
                            String command = Arrays.stream(CommandEnum.values()).map(item -> item.getCommand() + "：" + item.getDesc()).collect(Collectors.joining("\t")) + "\n";
                            String games = AbstractPlay.getValues().entrySet().stream().map(item -> item.getKey() + ": " + item.getValue().name()).collect(Collectors.joining("\t")) + "\n";
                            String welcome = "欢迎来到摸鱼世界：\n";
                            MessageResponse.putMessage(uuid, String.format("%s%s%s", welcome, command, games));
                            clientChannel.register(selector, SelectionKey.OP_WRITE, uuid);
                            log.info("%s 用户连接!", uuid);
                        }
                        if (selectionKey.isReadable()) {
                            SocketChannel clientChannel = (SocketChannel)selectionKey.channel();
                            SelectionKey key = clientChannel.keyFor(selector);
                            Object attachment = key.attachment();
                            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                            int len = clientChannel.read(readBuffer);
                            if (len > 0) {
                                String message = new String(readBuffer.array(), 0, len, StandardCharsets.UTF_8).replaceAll("\\r\\n", "").trim();
                                log.info("%s 用户输入了: %s", attachment.toString(), message);
                                CommandEnum command = CommandEnum.command(message);
                                if (command != null) {
                                    try {
                                        ExecuteCommand.execute(command, attachment.toString());
                                        clientChannel.register(selector, SelectionKey.OP_WRITE, attachment.toString());
                                    } catch (Exception exception) {
                                        exception.printStackTrace();
                                    }
                                    continue;
                                }
                                if (socketSession.containsKey(attachment.toString())) {
                                    AbstractPlay abstractPlay = (AbstractPlay)socketSession.get(attachment.toString());
                                    String resp = abstractPlay.next(message);
                                    MessageResponse.putMessage(attachment.toString(), resp);
                                    socketSession.put(attachment.toString(), abstractPlay);
                                    if (abstractPlay.isEnd()) {
                                        socketSession.remove(attachment.toString());
                                        MessageResponse.putMessage(attachment.toString(), nextSession());
                                    }
                                    clientChannel.register(selector, SelectionKey.OP_WRITE, attachment.toString());

                                    continue;
                                } else {
                                    AbstractPlay abstractPlay = AbstractPlay.getValues().get(message);
                                    if (abstractPlay != null) {
                                        log.info("%s 用户选择了[%s]", attachment.toString(), abstractPlay.name());
                                        abstractPlay = abstractPlay.clone();
                                        socketSession.put(attachment.toString(), abstractPlay);
                                        String resp = abstractPlay.first(null);
                                        MessageResponse.putMessage(attachment.toString(), resp);
                                        clientChannel.register(selector, SelectionKey.OP_WRITE, attachment.toString());
                                        continue;
                                    }
                                }
                                log.info("%s 用户输入指令无效! %s", attachment.toString(), message);
                            }
                            clientChannel.register(selector, SelectionKey.OP_READ, attachment.toString());
                        }
                        if (selectionKey.isWritable()) {
                            SocketChannel clientChannel = (SocketChannel)selectionKey.channel();
                            String attachment = clientChannel.keyFor(selector).attachment().toString();
                            String message = MessageResponse.getMessage(attachment);
                            log.info("%s 响应用户数据: %s", attachment, message);
                            if (message != null && message.length() > 0) {
                                ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                                writeBuffer.put(("\r\n" + message).replaceAll("\n", "\r\n").getBytes(Charset.forName("GBK")));
                                writeBuffer.flip();
                                clientChannel.write(writeBuffer);
                            }
                            if ("再见！".equals(message)) {
                                log.info("%s 用户断开了连接!", attachment);
                                MessageResponse.remote(attachment);
                                clientChannel.close();
                            } else {
                                clientChannel.register(selector,
                                        MessageResponse.hasMessage(attachment) ? SelectionKey.OP_WRITE : SelectionKey.OP_READ
                                        , clientChannel.keyFor(selector).attachment().toString());
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                }
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static String nextSession() {
        String command = Arrays.stream(CommandEnum.values()).map(item -> item.getCommand() + "：" + item.getDesc()).collect(Collectors.joining("\t")) + "\n";
        String games = AbstractPlay.getValues().entrySet().stream().map(item -> item.getKey() + ": " + item.getValue().name()).collect(Collectors.joining("\t")) + "\n";
        return String.format("%s%s", command, games);
    }
}
